/*
 * ICS 314 Avionics Assignment
 * @author ICS 314 Team Haines {Kevon Adonis, Alex Char, David Teroaka, Samuel Roberts}
 * @copyright 2014
 */

package avionics;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.border.TitledBorder;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Random;


public class AvionicsGUI implements ActionListener, ImageObserver{

    public static final String APP_TITLE = "AVIONICS v.1.0";
    public static JPanel gui;
    public static JProgressBar progressBar;
    public static JMenuBar topMenu;
    private final JMenu file;
    private final JMenu settings;
    private final JMenu help;
    private final JMenuItem fileOne;
    private final JMenuItem settingsOne;
    private final JMenuItem settingsTwo;
    private final JMenuItem helpOne;
    private final JMenuItem helpTwo;
    private final Random rng = new Random();

    // jPanels
    JPanel infoView;
    JPanel vorView;
    JPanel sideBarView;

    // pop up selector
    public static JPanel selectorBox;
    JComboBox radioList;
    public static JPanel instructionBox;
    public static JPanel signalBox;
    public static JPanel infoBox;
    AvionicsRadioSimulator radio = null;
    JLabel signalLabel;
    JLabel obsLabel;
    String radioSignal;

    /*
     * Returns a JPanel to be added to the GUI that displays the radio station selection
     * @param obsRadial The radial value returned from the radio
     */
    public JPanel selectorBox(){
        selectorBox =  new JPanel(new BorderLayout(1,1));
        TitledBorder title = BorderFactory.createTitledBorder("Radio Station");
        selectorBox.setBorder(title);
        String toolTip = "Select a radio station to receive VOR transmissions and radial degrees";
        selectorBox.setToolTipText(toolTip);
        String[] radioStations = { "Choose Station", "SkyNet", "Telnet", "SkyScan", "DeltaTek", "FlyWare" };
        radioList = new JComboBox(radioStations);
        radioList.setSelectedIndex(0);
        radioList.addActionListener(this);
        selectorBox.add(radioList);
        return selectorBox;
    }

    /*
     * Returns a JPanel to be added to the GUI that displays the radial data from the RadioSimulator
     * @param obsRadial The radial value returned from the radio
     */
    public JPanel instructionBox(int obsRadial){
        instructionBox =  new JPanel(new BorderLayout(1,1));
        TitledBorder title = BorderFactory.createTitledBorder("VOR Information");
        instructionBox.setBorder(title);
        String toolTip = "Set your OBS to the display radial and proceed";
        instructionBox.setToolTipText(toolTip);
        obsLabel = new JLabel("OBS Setting: " + Integer.toString(obsRadial));
        instructionBox.add(obsLabel);
        //repaint();
        return instructionBox;
    }
    /*
     * Returns a JPanel to be added to the GUI that displays the signal from the RadioSimulator
     * getSignalQuality() is called on the radio every 1000 nanoseconds and updated to the GUI
     */
    public JPanel signalBox(){
        Timer timer = new Timer();
        signalBox =  new JPanel(new BorderLayout(1,1));
        TitledBorder title = BorderFactory.createTitledBorder("Radio Signal");
        signalBox.setBorder(title);
        signalLabel = new JLabel();
        signalBox.add(signalLabel);
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
              radioSignal = radio.getSignalQuality(rng.nextInt(10));
              signalLabel.setText(radioSignal);
              System.out.println("Signal: " + radioSignal);
            }
          }, 500, 1000);
        signalBox.setToolTipText(radioSignal);
        return signalBox;
    }

     /*
     * Returns a JPanel to be added to the GUI that displays general information about the program
     */
    public JPanel infoBox(){
        infoBox =  new JPanel(new GridLayout(1,1));
        TitledBorder title = BorderFactory.createTitledBorder("General Info");
        infoBox.setBorder(title);
        JLabel info = new JLabel();
        info.setBounds(0, 20, 130, 50);
        info.setText("<html>This program <br>demonstrate navigation <br>using a VOR system. <br>After a radio station <br>is selected it...!</html>");
        infoBox.add(info);
        return infoBox;
    }





    public AvionicsGUI(){
        gui = new JPanel(new BorderLayout(3,2));
        topMenu = new JMenuBar();
        file = new JMenu("File");
        file.setMnemonic('F');
        settings = new JMenu("Settings");
        settings.setMnemonic('S');
        help = new JMenu("Help");
        help.setMnemonic('H');
        fileOne = new JMenuItem("Exit");
        fileOne.addActionListener(this);
        settingsOne = new JMenuItem("Change Radio");
        settingsOne.addActionListener(this);
        settingsOne.setEnabled(false);
        settingsTwo = new JMenuItem("Refresh");
        settingsTwo.addActionListener(this);
        helpOne = new JMenuItem("How to use");
        helpOne.addActionListener(this);
        helpTwo = new JMenuItem("About Avionics");
        helpTwo.addActionListener(this);
        file.add(fileOne);
        settings.add(settingsOne);
        settings.add(settingsTwo);
        help.add(helpOne);
        help.add(helpTwo);
        topMenu.add(file);
        topMenu.add(settings);
        topMenu.add(help);


        infoView = new JPanel(new BorderLayout(1,1));
        vorView = new JPanel(new BorderLayout(1,1));
        sideBarView = new JPanel(new GridLayout(6,1));
        infoView.add(infoBox());
        vorView.add(new VOR(65));
        sideBarView.add(selectorBox());


        gui.add(topMenu, BorderLayout.NORTH);
        gui.add(infoView, BorderLayout.WEST);
        gui.add(vorView, BorderLayout.CENTER);
        gui.add(sideBarView, BorderLayout.EAST);
        }

        public Container getGui(){
            return gui;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if(e.getSource() instanceof JComboBox){
                JComboBox cb = (JComboBox)e.getSource();
                String radioStation = (String)cb.getSelectedItem();
                radioList.setEnabled(false);
                radio = new AvionicsRadioSimulator(radioStation, "435");
                sideBarView.add(instructionBox(radio.getRadial()));
                sideBarView.add(signalBox());
            }

            if(e.getSource() == fileOne){
                System.exit(0);
            }else if(e.getSource() == helpOne){
             JOptionPane.showMessageDialog(getGui(),
                "Our VOR simulator contains an interface to display what a pilot would see \n"
                + "after tuning in to a VOR radio station. \n" +
                "Once the VOR program is loaded, the user needs to select a radio station \n"
                + "from a list of preset stations. \n" +
                "After selection, the system shows the intended OBS setting and waits for \n"
                + "the user to set the dial to the desired setting. \n" +
                "Internally the program generates a random directional radial ranging \n"
                + "from 0 to 359 as a simulated value to that of a real VOR radio station. \n" +
                "Users are asked to keep an eye on the display for the radio signal \n"
                + "reading which indicates signal strength from the VOR station. \n",
                "How to use this program", JOptionPane.PLAIN_MESSAGE);

            }else if(e.getSource() == helpTwo){
             JOptionPane.showMessageDialog(getGui(),
                "Our Avionics package was developed in Java using elements of \n"
                + "the swing class for on screen drawings and on-the-fly updates. \n"
                + "Our team used JUnit test cases to ensure the program functions \n"
                + "according to the specifications set forth.\n"
                + "Our VOR program will help an airplane navigate from it’s current \n"
                + "position to another position relative to a VOR radio station. \n"
                + "These radio stations exist all over the country, and are essential \n"
                + "to the navigation process. The program takes an input from the user \n"
                + "in the form of a dial, and it’s output is displayed as a signal \n"
                + "strength as well as an arrow which indicates what direction the \n"
                + "plane should be headed.","About Avionics", JOptionPane.PLAIN_MESSAGE);

            }

        }

    @Override
    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}


class VOR extends JComponent implements MouseListener, MouseMotionListener {

    private static int radius;
    private static int spotRadius;
    private double angle;
    private final Color knobColor;
    private final Color spotColor;
    private boolean pressedOnSpot;

    BufferedImage courseCard = null;
    BufferedImage foreground = null;
    BufferedImage needle = null;
    Graphics2D g2d;

    /**
     * Constructor that initializes the position
     * of the knob to the specified angle in radians.
     *
     * @param initAngle the initial angle of the knob.
     */
    public VOR(int diameter) {
        angle = 0;
        pressedOnSpot = false;
        knobColor = Color.gray;
        spotColor = Color.DARK_GRAY;
        spotRadius = (int) (diameter * .2);
        radius = diameter / 2;
        this.addMouseListener(this);
        this.addMouseMotionListener(this);

         try {
            courseCard = ImageIO.read(new File("background.png"));
            foreground = ImageIO.read(new File("foreground.png"));
            needle = ImageIO.read(new File("needle.png"));
        } catch (IOException ex) {
            Logger.getLogger(VOR.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    /**
     * Paint the JKnob on the graphics context given.  The knob
     * is a filled circle with a small filled circle offset
     * within it to show the current angular position of the
     * knob.
     *
     * @param g The graphics context on which to paint the knob.
     */
    @Override
    public void paint(Graphics g) {
        // Draw the knob.
        g.setColor(knobColor);
        g.fillOval(0,0,2*radius,2*radius);

        // Find the center of the spot.
        Point pt = getSpotCenter();
        int xc = (int)pt.getX();
        int yc = (int)pt.getY();

        // Draw the spot.
        g.setColor(spotColor);
        g.fillOval(xc-spotRadius, yc-spotRadius, 2*spotRadius, 2*spotRadius);
        g.setColor(Color.white);
        g.drawString("OBS", 23, 35);


        AffineTransform identity = new AffineTransform();
        g2d = (Graphics2D)g;


        AffineTransform trans = new AffineTransform();
        trans.setTransform(identity);
        trans.rotate( getAngle(), 200, 200 );
        g2d.drawImage(courseCard, trans, this);
        g2d.drawImage(foreground, identity, this);
        g2d.drawImage(needle, identity, this);

    }

    /**
     * Return the ideal size that the knob would like to be.
     *
     * @return the preferred size of the JKnob.
     */
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(2*radius,2*radius);
    }

    /**
     * Return the minimum size that the knob would like to be.
     * This is the same size as the preferred size so the
     * knob will be of a fixed size.
     *
     * @return the minimum size of the JKnob.
     */
    @Override
    public Dimension getMinimumSize() {
        return new Dimension(2*radius,2*radius);
    }

    /**
     * Get the current angular position of the knob.
     * @return the current angular position of the knob.
     */
    public double getAngle() {
        return angle;
    }

    /**
     * Calculate the x, y coordinates of the center of the spot.
     * @return a Point containing the x,y position of the center
     *         of the spot.
     */
    private Point getSpotCenter() {
        int r = radius - spotRadius;
        int xcp = (int)(r * Math.sin(angle));
        int ycp = (int)(r * Math.cos(angle));
        int xc = radius + xcp;
        int yc = radius - ycp;
        return new Point(xc,yc);
    }

    /**
     * Determine if the mouse click was on the spot or
     * not.  If it was return true, otherwise return
     * false.
     * @return true if x,y is on the spot and false if not.
     */
    private boolean isOnSpot(Point pt) {
        return (pt.distance(getSpotCenter()) < spotRadius);
    }

    @Override
    public void mouseClicked(MouseEvent e) {}


    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}


    @Override
    public void mousePressed(MouseEvent e) {
        Point mouseLoc = e.getPoint();
        pressedOnSpot = isOnSpot(mouseLoc);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        pressedOnSpot = false;
    }

    @Override
    public void mouseMoved(MouseEvent e) {}


    @Override
    public void mouseDragged(MouseEvent e) {
        if (pressedOnSpot) {
            int mx = e.getX();
            int my = e.getY();
            int mxp = mx - radius;
            int myp = radius - my;
            angle = Math.atan2(mxp, myp);
            AffineTransform identity = new AffineTransform();
            AffineTransform trans = new AffineTransform();
            trans.setTransform(identity);
            trans.rotate( getAngle(), 200, 200 );
            g2d.drawImage(courseCard, trans, this);
            repaint();
        }
    }
}